package com.superb.common.database.inner;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.BaseMultiTableInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.superb.common.database.annotation.DelInterceptorIgnore;
import com.superb.common.database.utils.BeanMethodUtils;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.WithItem;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

/**
 * 删除拦截器，照抄TenantLineInnerInterceptor，主要对自定义租户拦截注解作用
 * @Author: ajie
 * @CreateTime: 2024-05-23 12:32
 */
@NoArgsConstructor
@AllArgsConstructor
public class DelLineInnerInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor {

    private TenantLineHandler tenantLineHandler;

    /**
     * 获取类id
     * @param ms
     * @return
     */
    @NonNull
    private String getId(MappedStatement ms) {
        String id = ms.getId();
        int index = id.lastIndexOf(StringPool.DOT);
        String className = id.substring(0, index);
        String methodName = id.substring(index);
        int methodIndex = methodName.lastIndexOf(StringPool.UNDERSCORE);
        if (methodIndex > -1) {
            methodName =methodName.substring(0, methodIndex);
        }
        id = className+methodName;
        return id;
    }

    /**
     * 查询前处理,如果存在删除拦截注解，则不做处理
     * @param executor
     * @param ms
     * @param parameter
     * @param rowBounds
     * @param resultHandler
     * @param boundSql
     * @throws SQLException
     */
    @Override
    @SneakyThrows
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        String id = this.getId(ms);
        Method method = BeanMethodUtils.getMethod(id, parameter);
        boolean annotationPresent = method.isAnnotationPresent(DelInterceptorIgnore.class);
        if (annotationPresent) {
            return;
        }
        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
        mpBs.sql(this.parserSingle(mpBs.sql(), null));
    }

    /**
     * 操作处理
     * @param sh
     * @param connection
     * @param transactionTimeout
     */
    @Override
    @SneakyThrows
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        // mapper处理器
        PluginUtils.MPStatementHandler statementHandler = PluginUtils.mpStatementHandler(sh);
        // 获取mapper执行器
        MappedStatement ms = statementHandler.mappedStatement();
        // 获取mapper执行方法类型
        SqlCommandType sqlCommandType = ms.getSqlCommandType();
        // 删除和修改时拦截
        if (sqlCommandType == SqlCommandType.DELETE || sqlCommandType == SqlCommandType.UPDATE) {
            String id = this.getId(ms);
            Method method = BeanMethodUtils.getMethod(id, statementHandler.parameterHandler().getParameterObject());
            boolean annotationPresent = method.isAnnotationPresent(DelInterceptorIgnore.class);
            if (annotationPresent) {
                return;
            }
            PluginUtils.MPBoundSql mpBs = statementHandler.mPBoundSql();
            mpBs.sql(this.parserSingle(mpBs.sql(), null));
        }
    }

    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {
        final String whereSegment = (String) obj;
        processSelectBody(select, whereSegment);
        List<WithItem> withItemsList = select.getWithItemsList();
        if (!CollectionUtils.isEmpty(withItemsList)) {
            withItemsList.forEach(withItem -> processSelectBody(withItem, whereSegment));
        }
    }

    /**
     * 处理删除语句
     * @param update
     * @param index
     * @param sql
     * @param obj
     */
    @Override
    protected void processUpdate(Update update, int index, String sql, Object obj) {
        final Table table = update.getTable();
        if (this.tenantLineHandler.ignoreTable(table.getName())) {
            // 过滤退出执行
            return;
        }
        update.setWhere(this.andExpression(table, update.getWhere(), (String)obj));
    }

    @Override
    protected void processDelete(Delete delete, int index, String sql, Object obj) {
        if (!this.tenantLineHandler.ignoreTable(delete.getTable().getName())) {
            delete.setWhere(this.andExpression(delete.getTable(), delete.getWhere(), (String)obj));
        }
    }

    @Override
    public Expression buildTableExpression(final Table table, final Expression where, final String whereSegment) {
        return this.tenantLineHandler.ignoreTable(table.getName()) ? null : new EqualsTo(this.getAliasColumn(table), this.tenantLineHandler.getTenantId());
    }

    /**
     * 删除字段别名设置
     * <p>del 或 tableAlias.del</p>
     *
     * @param table 表对象
     * @return 字段
     */
    protected Column getAliasColumn(Table table) {
        StringBuilder column = new StringBuilder();
        if (table.getAlias() != null) {
            column.append(table.getAlias().getName()).append(".");
        }
        column.append(this.tenantLineHandler.getTenantIdColumn());
        return new Column(column.toString());
    }

}
